using System;
using System.Threading;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace Derrick.Web
{
    abstract public class Server
    {
        protected Thread _listenerThread = null;
		protected TcpListener listener = null;
        protected AuthConfig authconfig;
        protected TraceSwitch _traceSwitch;
        protected List<Client> clients = null;
        protected volatile object _lockObj = new object();
        protected bool _isListenerShutDown = false;
        protected static Mutex _listenerMutex = new Mutex();

        protected int _currentClientId = 0;

        public bool IsListenerShutDown
        {
            get { return _isListenerShutDown; }
            set { _isListenerShutDown = value; }
        }

        public Mutex ListenerMutex
        {
            get { return _listenerMutex; }
        }


		public Server(IPAddress ip, Int32 port, AuthConfig a)
		{

            if (null == clients)
            {
                clients = new List<Client>();
            }

            try
            {
                _traceSwitch = new TraceSwitch("TraceLevelSwitch", "Comet Tracing");
                
                authconfig = a;



                //listener = new TcpListener(port);
                listener = new TcpListener(ip, port);
                listener.ExclusiveAddressUse = true;
                listener.Start();

                _listenerThread = new Thread(new ThreadStart(Loop));
                _listenerThread.Start();


            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("Derrick COMET", ex.ToString(), EventLogEntryType.Error);
                Trace.WriteLineIf(_traceSwitch.TraceError, "Error starting on port " + port + ": " + ex.ToString(), "COMET Server");
            }

		}
				
		public virtual void Loop()
		{
            try
            {
                //List<Client> clients = new List<Client>();

                while (true)
                {


                    TcpClient client = null;

                    bool isServerStopped = false;
                    _listenerMutex.WaitOne();
                    isServerStopped = _isListenerShutDown;
                    _listenerMutex.ReleaseMutex();

                    if (!isServerStopped)
                    {
                        client = listener.AcceptTcpClient();
                    }
                    else
                    {
                        continue;
                    }

                    Trace.WriteLineIf(_traceSwitch.TraceInfo, "TCP client accepted.", "COMET Server");

                    bool addClientFlag = true;

                    Client dc = new Client(client, this, authconfig, _currentClientId);
                    _currentClientId++;

                    for (int i = 0; i < clients.Count; i++)
                    {

                        if (!clients[i].IsLive || !clients[i].TCPClient.Connected)
                        {
                            clients[i].CleanUp();
                            clients.RemoveAt(i);
                        }

                        if (dc.TCPClient == clients[i].TCPClient)
                        {
                            lock (_lockObj)
                            {
                                addClientFlag = false;
                                //break;
                            }
                        }


                    }

                    if (addClientFlag)
                    {
                        lock (_lockObj)
                        {
                            clients.Add(dc);
                        }
                    }

                    Trace.WriteLineIf(_traceSwitch.TraceInfo, "Number of TCP clients: " + clients.Count);


                }
            }
            catch (Exception ex)
            {
                //EventLog.WriteEntry("Derrick COMET", ex.ToString(), EventLogEntryType.Error);
                Trace.WriteLineIf(_traceSwitch.TraceError, ex.ToString(), "COMET Server");
            }
		}

		
		public bool IsRunning()
		{
			return _listenerThread.IsAlive;
		}



        public void CloseListener()
        {
            if (listener != null)
            {
                _listenerMutex.WaitOne();
                _isListenerShutDown = true;
                listener.Stop();
                _listenerMutex.ReleaseMutex();
            }
        }

        public void ShutDown()
        {
            CloseListener();
            foreach (Client c in clients)
            {
                if (c != null)
                {
                    c.CleanUp();
                }
            }
        }

        abstract public Response HandleRequest(Request request, DateTime lastUpdate, Client cometClient);

        public void BroadcastMessage(object sender, SendMessageEventArgs args)
        {
            //throw new NotImplementedException();

            Trace.WriteLineIf(_traceSwitch.TraceVerbose, "Broadcasting to all web sockets.");

            lock (_lockObj)
            {
                foreach (Client client in clients)
                {
                    ParameterizedThreadStart messageThread = new ParameterizedThreadStart(client.SendResponseThread);
                    Thread broadcastThread = new Thread(messageThread);
                    broadcastThread.Start(args);

                }

            }

        }


    }
}
